package com.atguigu.tingshu.search.service.impl;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.util.PinYinUtils;
import com.atguigu.tingshu.search.repository.SuggestIndexRepository;
import com.atguigu.tingshu.vo.album.AlbumStatVoMsg;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import cn.hutool.core.util.PageUtil;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.aggregations.*;
import co.elastic.clients.elasticsearch.core.search.*;
import co.elastic.clients.elasticsearch.core.termvectors.Term;
import co.elastic.clients.json.JsonData;
import com.atguigu.tingshu.model.album.BaseCategory3;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexSearchVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.google.common.collect.Lists;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldSort;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.CategoryFeignClient;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.config.ThreadPoolExecutorConfiguration;
import com.atguigu.tingshu.search.repository.AlbumIndexRepository;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumStatVo;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import io.micrometer.common.util.StringUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

/**
 * 方案二：使用CompletableFuture以是的多线程按照需要先后执行，并保证的多线程的异步执行，保证的需求的同时有兼顾程序执行效率
 */
@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class SearchServiceImpl implements SearchService {
    //es调用增删修改Es连接库中的数据
    @Autowired
    AlbumIndexRepository albumIndexRepository;
    //远程服务调用
    @Autowired
    AlbumInfoFeignClient albumInfoFeignClient;
    @Autowired
    CategoryFeignClient categoryFeignClient;
    @Autowired
    UserInfoFeignClient userInfoFeignClient;

    @Autowired
    ElasticsearchClient elasticsearchClient;

    @Autowired
    SuggestIndexRepository suggestIndexRepository;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;


    @Override
    @SneakyThrows
    public void upperAlbum(Long albumId) {
        //构建AlbumInfoIndex对象
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();

        //将查询albumInfo专辑基本数据作为先遣任务，以供后续其他任务使用
        CompletableFuture<AlbumInfo> c1 = CompletableFuture.supplyAsync(() -> {
            //专辑基本数据，请求专辑微服务根据专辑的id查询专辑基本数据
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfoById(albumId);
            AlbumInfo albumInfo = albumInfoResult.getData();
            BeanUtils.copyProperties(albumInfo, albumInfoIndex);
            albumInfoIndex.setIsFinished(String.valueOf(albumInfo.getIsFinished()));

            //请求专辑微服务根据专辑id获取专辑属性值列表
            List<AlbumAttributeValue> attributeValueList = albumInfo.getAlbumAttributeValueVoList();
            //将AlbumAttributeValue中的属性ID和属性值ID复制到attributeValueIndex中
            List<AttributeValueIndex> attributeValueIndexList = attributeValueList.stream().map(attributeValue -> {
                AttributeValueIndex attributeValueIndex = new AttributeValueIndex();
                BeanUtils.copyProperties(attributeValue, attributeValueIndex);
                return attributeValueIndex;
            }).toList();
            albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);
            return albumInfo;
        });

        //在通过上面的任务获取albumInfo专辑基本数据后紧接着执行的任务1
        CompletableFuture<Void> c2 = c1.thenAcceptAsync(albumInfo -> {
            //请求专辑微服务根据专辑三级分类的id查询专辑分类信息
            Result<BaseCategoryView> baseCategoryViewResult = categoryFeignClient.findCategoryByC3Id(albumInfo.getCategory3Id());
            BaseCategoryView baseCategoryView = baseCategoryViewResult.getData();
            albumInfoIndex.setCategory1Id(baseCategoryView.getCategory1Id());
            albumInfoIndex.setCategory2Id(baseCategoryView.getCategory2Id());
            albumInfoIndex.setCategory3Id(baseCategoryView.getCategory3Id());
        });
        //在通过上面的任务获取albumInfo专辑基本数据后紧接着执行的任务2
        CompletableFuture<Void> c3 = c1.thenAcceptAsync(albumInfo -> {
            //请求用户微服务根据专辑作者id获取用户数据
            Result<UserInfo> userInfoResult = userInfoFeignClient.findUserInfoById(albumInfo.getUserId());
            UserInfo userInfo = userInfoResult.getData();
            albumInfoIndex.setAnnouncerName(userInfo.getNickname());
        });
        //与前面的3个任务并发执行的任务4
        CompletableFuture<Void> c4 = CompletableFuture.runAsync(() -> {
            //请求专辑微服务根据专辑id获取专辑的统计数据
            Result<AlbumStatVo> albumStatVoResult = albumInfoFeignClient.findAlbumStatByAlbumId(albumId);
            AlbumStatVo albumStatVo = albumStatVoResult.getData();
            Integer playStatNum = albumStatVo.getPlayStatNum();
            albumInfoIndex.setPlayStatNum(playStatNum);
            Integer subscribeStatNum = albumStatVo.getSubscribeStatNum();
            albumInfoIndex.setSubscribeStatNum(subscribeStatNum);
            Integer buyStatNum = albumStatVo.getBuyStatNum();
            albumInfoIndex.setBuyStatNum(buyStatNum);
            Integer commentStatNum = albumStatVo.getCommentStatNum();
            albumInfoIndex.setCommentStatNum(commentStatNum);
            albumInfoIndex.setHotScore(playStatNum * 1.0 + subscribeStatNum * 2.0 + buyStatNum * 3.0 + commentStatNum * 4.0);
        });

        //阻塞等待至到前面的4个任务全部执行结束后才解除阻塞继续向下执行，注意由于c2,c3任务的执行需要c1任务执行的结果，故以下可不写c1任务
        CompletableFuture.allOf(c1,c2,c3,c4).join();
        log.info(JSON.toJSONString(albumInfoIndex));
        //将AlbumInfoIndex对象保存到ES索引库中
        albumIndexRepository.save(albumInfoIndex);

        //初始化自动补全词库
        CompletableFuture.runAsync(()->{
                initSuggestinfo(albumInfoIndex);}
        );
        //将当前专辑的Id存储到布隆过滤器中，
        RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
        boolean isBloomFilterAdd = bloomFilter.add(albumId);
        if(isBloomFilterAdd){
            log.info("向布隆过滤器中添加数据成功");
        }
    }

    private void initSuggestinfo(AlbumInfoIndex albumInfoIndex) {
        //保存专辑数据的AlbumTitle数据到Suggestinfo词库中
        SuggestIndex titleSuggestIndex = new SuggestIndex();
        titleSuggestIndex.setId(UUID.randomUUID().toString().replace("-",""));
        titleSuggestIndex.setTitle(albumInfoIndex.getAlbumTitle());
        titleSuggestIndex.setKeyword(new Completion(new String[]{albumInfoIndex.getAlbumTitle()}));
        titleSuggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumInfoIndex.getAlbumTitle())}));
        titleSuggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumInfoIndex.getAlbumTitle())}));
        suggestIndexRepository.save(titleSuggestIndex);
        //保存专辑数据的AlbumIntro数据到Suggestinfo词库中
        SuggestIndex introSuggestIndex = new SuggestIndex();
        introSuggestIndex.setId(UUID.randomUUID().toString().replace("-",""));
        introSuggestIndex.setTitle(albumInfoIndex.getAlbumIntro());
        introSuggestIndex.setKeyword(new Completion(new String[]{albumInfoIndex.getAlbumIntro()}));
        introSuggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumInfoIndex.getAlbumIntro())}));
        introSuggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumInfoIndex.getAlbumIntro())}));
        suggestIndexRepository.save(introSuggestIndex);
        //保存专辑数据的作者名称数据到Suggestinfo词库中
        SuggestIndex announcerNameSuggestIndex = new SuggestIndex();
        announcerNameSuggestIndex.setId(UUID.randomUUID().toString().replace("-",""));
        announcerNameSuggestIndex.setTitle(albumInfoIndex.getAnnouncerName());
        announcerNameSuggestIndex.setKeyword(new Completion(new String[]{albumInfoIndex.getAnnouncerName()}));
        announcerNameSuggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumInfoIndex.getAnnouncerName())}));
        announcerNameSuggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumInfoIndex.getAnnouncerName())}));
        suggestIndexRepository.save(announcerNameSuggestIndex);
    }

    @Override
    public void lowerAlbum(Long albumId) {
        albumIndexRepository.deleteById(albumId);
        /**
         * 对布隆过滤器进行重建 重建的时机为专辑下架时
         */
        RBloomFilter<Long> bloomFilterNew = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER_NEW);
        boolean isBloomFilterTryInitNew = bloomFilterNew.tryInit(1000000, 0.00001); //参数1：存储的数据个数，参数2：误判率
        if(isBloomFilterTryInitNew){
            log.info("新的布隆过滤器初始化成功");
        }
        albumIndexRepository.findAll().forEach(albumInfoIndex->bloomFilterNew.add(albumInfoIndex.getId()));
        String script = "redis.call(\"del\" , KEYS[1])\n" +
                "redis.call(\"del\" , \"{\"..KEYS[1]..\"}:\"..\"config\")\n" +
                "redis.call(\"rename\" , KEYS[2] , KEYS[1])\n" +
                "redis.call(\"rename\" , \"{\"..KEYS[2]..\"}:\"..\"config\" , \"{\"..KEYS[1]..\"}:\"..\"config\")\n" +
                "return 1" ;
        Long result = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(RedisConstant.ALBUM_BLOOM_FILTER, RedisConstant.ALBUM_BLOOM_FILTER_NEW));
        if (result.equals(1)) {
            log.info("布隆过滤器重置成功");
        }
    }
    @SneakyThrows
    @Override
    public AlbumSearchResponseVo search(AlbumIndexQuery albumIndexQuery) {
        SearchRequest searchRequest = buildSearchRequest(albumIndexQuery);
        SearchResponse<AlbumInfoIndex> indexSearchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);

        return ParseSearchResponse(indexSearchResponse,albumIndexQuery);
    }

    @Override
    @SneakyThrows
    public List<AlbumInfoIndexSearchVo> channel(Long category1Id) {
        Result<List<BaseCategory3>> topBaseCategory3Result = categoryFeignClient.findTopBaseCategory3(category1Id);
        List<BaseCategory3> baseCategory3List = topBaseCategory3Result.getData();
        Map<Long, BaseCategory3> collect = baseCategory3List.stream().collect(Collectors.toMap(baseCategory3 -> baseCategory3.getId(), baseCategory3 -> baseCategory3));
        Set<Long> c3Ids = collect.keySet();
        List<FieldValue> fieldValueList = c3Ids.stream().map(c3Id -> {
            return new FieldValue.Builder().longValue(c3Id).build();
        }).toList();
        TermQuery termQuery = new TermQuery.Builder().field("category1Id").value(category1Id).build();
        Query query1 = new Query.Builder().term(termQuery).build();
        TermsQueryField termsQueryField = new TermsQueryField.Builder().value(fieldValueList).build();
        TermsQuery termsQuery = new TermsQuery.Builder().field("category3Id").terms(termsQueryField).build();
        Query query2 = new Query.Builder().terms(termsQuery).build();
        BoolQuery boolQuery = new BoolQuery.Builder().must(query1).must(query2).build();
        Query query = new Query.Builder().bool(boolQuery).build();
        SourceConfig sourceConfig = new SourceConfig.Builder().fetch(false).build();
        TermsAggregation termsAggregation = new TermsAggregation.Builder().field("category3Id").size(6).build();
        SortOptions sortOptions = new SortOptions.Builder().field(builder ->builder.field("hotScore").order(SortOrder.Desc)).build();
        TopHitsAggregation topHitsAggregation = new TopHitsAggregation.Builder().size(6).sort(sortOptions).build();
        Aggregation aggregation1 = new Aggregation.Builder().topHits(topHitsAggregation).build();
        Aggregation aggregation = new Aggregation.Builder().terms(termsAggregation).aggregations("hostScoreAlbum",aggregation1).build();
        SearchRequest searchRequest = new SearchRequest.Builder().index("albuminfo").query(query).source(sourceConfig).aggregations("baseCategoryC3IdAgg",aggregation).build();
        SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);
        Map<String, Aggregate> aggregateMap = searchResponse.aggregations();
        Aggregate aggregate = aggregateMap.get("baseCategoryC3IdAgg");
        LongTermsAggregate longTermsAggregate = (LongTermsAggregate) aggregate._get();
        Buckets<LongTermsBucket> buckets = longTermsAggregate.buckets();
        List<LongTermsBucket> bucketList = buckets.array();
        List<AlbumInfoIndexSearchVo> albumInfoIndexSearchVos = new ArrayList<>();
        for (LongTermsBucket bucket : bucketList) {
            long c3Id = bucket.key();
            Map<String, Aggregate> subAggMap = bucket.aggregations();
            Aggregate hostScoreAlbumAggregate = subAggMap.get("hostScoreAlbum");
            TopHitsAggregate topHitsAggregate = (TopHitsAggregate) hostScoreAlbumAggregate._get();
            HitsMetadata<JsonData> hitsMetadata = topHitsAggregate.hits();
            List<Hit<JsonData>> hitList = hitsMetadata.hits();
            List<AlbumInfoIndex> albumInfoIndexList = hitList.stream().map(hit -> {
                return hit.source().to(AlbumInfoIndex.class);
            }).toList();
            AlbumInfoIndexSearchVo albumInfoIndexSearchVo = new AlbumInfoIndexSearchVo();
            albumInfoIndexSearchVo.setBaseCategory3(collect.get(c3Id));
            albumInfoIndexSearchVo.setList(albumInfoIndexList);
            albumInfoIndexSearchVos.add(albumInfoIndexSearchVo);
        }
        return albumInfoIndexSearchVos;
    }

    @SneakyThrows
    @Override
    public List<String> completeSuggest(String keyword) {
        SuggestFuzziness suggestFuzziness = new SuggestFuzziness.Builder().fuzziness("auto").build();
        CompletionSuggester completionSuggester = new CompletionSuggester.Builder().field("keyword").size(10).skipDuplicates(true).fuzzy(suggestFuzziness).build();
        CompletionSuggester completionSuggesterPinyin = new CompletionSuggester.Builder().field("keywordPinyin").size(10).skipDuplicates(true).fuzzy(suggestFuzziness).build();
        CompletionSuggester completionSuggesterSequence = new CompletionSuggester.Builder().field("keywordSequence").size(10).skipDuplicates(true).fuzzy(suggestFuzziness).build();
        FieldSuggester fieldSuggester = new FieldSuggester.Builder().prefix(keyword).completion(completionSuggester).build();
        FieldSuggester fieldSuggesterPinyin = new FieldSuggester.Builder().prefix(keyword).completion(completionSuggesterPinyin).build();
        FieldSuggester fieldSuggesterSequence = new FieldSuggester.Builder().prefix(keyword).completion(completionSuggesterSequence).build();
        Suggester suggester = new Suggester.Builder().suggesters("keywordSuggest", fieldSuggester).suggesters("keywordPinyinSuggest",fieldSuggesterPinyin).suggesters("keywordSequenceSuggest",fieldSuggesterSequence).build();
        SearchRequest searchRequest = new SearchRequest.Builder().index("suggestinfo").suggest(suggester).build();
        SearchResponse<SuggestIndex> searchResponse = elasticsearchClient.search(searchRequest, SuggestIndex.class);
        Map<String, List<Suggestion<SuggestIndex>>> stringListMap = searchResponse.suggest();
        List<String> stringList =  new ArrayList<>();
        parseSearchResponse(stringList,stringListMap,"keywordSuggest");
        parseSearchResponse(stringList,stringListMap,"keywordPinyinSuggest");
        parseSearchResponse(stringList,stringListMap,"keywordSequenceSuggest");
        return stringList;
    }

    @Override
    public void updateLatelyAlbumRanking() {
        try {
            // 创建按照一级分类的id进行聚合的聚合对象
            Aggregation.Builder.ContainerBuilder category1IdAggBuilder = new Aggregation.Builder()
                    .terms(AggregationBuilders.terms().field("category1Id").size(100).build());

            // 定义统计维度
            String[] rankingDimensionArray = new String[]{ "hotScore" , "playStatNum" , "subscribeStatNum" , "buyStatNum", "commentStatNum"} ;
            for(String rankingDimension : rankingDimensionArray) {

                // 构建排序字段对象
                FieldSort fieldSort = new FieldSort.Builder().field(rankingDimension).order(SortOrder.Desc).build();
                SortOptions sortOptions = new SortOptions.Builder().field(fieldSort).build() ;

                // 构建子聚合对象
                Aggregation topHitsAggregation = new Aggregation.Builder()
                        .topHits(AggregationBuilders.topHits().size(10).sort(sortOptions).build()).build();

                // 添加子聚合
                category1IdAggBuilder.aggregations(rankingDimension + "TopHitsAgg" , topHitsAggregation) ;

            }
            // 创建SearchRequest对象
            SourceConfig sourceConfig = new SourceConfig.Builder().fetch(false).build();
            SearchRequest searchRequest = new SearchRequest.Builder()
                    .index("albuminfo")
                    .source(sourceConfig)
                    .aggregations("category1IdAgg" , category1IdAggBuilder.build())
                    .build() ;

            // 发送搜索请求
            SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);

            // 解析结果
            Map<String, Aggregate> aggregations = searchResponse.aggregations();
            Aggregate category1IdAggregate = aggregations.get("category1IdAgg");
            LongTermsAggregate category1IdAgg = (LongTermsAggregate) category1IdAggregate._get() ;
            List<LongTermsBucket> termsBuckets = category1IdAgg.buckets().array();
            for(LongTermsBucket longTermsBucket : termsBuckets) {

                // 获取一级分类的id
                long category1Id = longTermsBucket.key();

                // 构建Redis的数据key
                String rankingRedisKey = RedisConstant.RANKING_KEY_PREFIX + category1Id ;

                // 获取各个维度的子聚合结果
                Map<String, Aggregate> aggregateMap = longTermsBucket.aggregations();
                for(String rankingDimension : rankingDimensionArray) {

                    // 从聚合结果中获取原始文档数据，然后将其存储到List集合中
                    Aggregate aggregate = aggregateMap.get(rankingDimension + "TopHitsAgg");
                    TopHitsAggregate topHitsAggregate = (TopHitsAggregate) aggregate._get();
                    List<Hit<JsonData>> hits = topHitsAggregate.hits().hits();
                    List<AlbumInfoIndex> albumInfoIndexList = hits.stream().map(hit -> {
                        String source = hit.source().toString();
                        return JSON.parseObject(source, AlbumInfoIndex.class);
                    }).collect(Collectors.toList());

                    // 将检索到的排行数据存储到Redis中
                    redisTemplate.opsForHash().put(rankingRedisKey , rankingDimension , JSON.toJSONString(albumInfoIndexList));

                }

            }

        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public List<AlbumInfoIndexVo> findRankingList(Long category1Id, Long dimension) {
        String rankingRedisKey = RedisConstant.RANKING_KEY_PREFIX + category1Id ;
        String jsonList = (String) redisTemplate.opsForHash().get(rankingRedisKey, dimension);
        if (!StringUtils.isEmpty(jsonList)) {
            List<AlbumInfoIndex> albumInfoIndexList = JSON.parseArray(jsonList, AlbumInfoIndex.class);
            return albumInfoIndexList.stream().map(albumInfoIndex -> {
                AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
                BeanUtils.copyProperties(albumInfoIndex, albumInfoIndexVo);
                return albumInfoIndexVo;
            }).toList();
        }
        return null;
    }

    @Override
    public void updateAlbumStat(AlbumStatVoMsg albumStatVoMsg) {
        Optional<AlbumInfoIndex> albumInfoIndexOptional = albumIndexRepository.findById(albumStatVoMsg.getAlbumId());
        AlbumInfoIndex albumInfoIndex = albumInfoIndexOptional.get();

        switch (albumStatVoMsg.getStatType()){
            case SystemConstant.ALBUM_STAT_PLAY : {
                albumInfoIndex.setPlayStatNum(albumStatVoMsg.getStatNum());
                break;
            }
            case SystemConstant.ALBUM_STAT_SUBSCRIBE: {
                albumInfoIndex.setSubscribeStatNum(albumStatVoMsg.getStatNum());
                break;
            }
            case SystemConstant.ALBUM_STAT_BROWSE: {
                albumInfoIndex.setBuyStatNum(albumStatVoMsg.getStatNum());
                break;
            }
            case SystemConstant.ALBUM_STAT_COMMENT: {
                albumInfoIndex.setCommentStatNum(albumStatVoMsg.getStatNum());
                break;
            }
        }
        albumIndexRepository.save(albumInfoIndex);
    }

    private void parseSearchResponse(List<String> stringList,Map<String, List<Suggestion<SuggestIndex>>> stringListMap,String suggestName){
        List<Suggestion<SuggestIndex>> suggestionList = stringListMap.get(suggestName);

        for (Suggestion<SuggestIndex> suggestion : suggestionList) {
            CompletionSuggest<SuggestIndex> completion = suggestion.completion();
            List<CompletionSuggestOption<SuggestIndex>> options = completion.options();
            for (CompletionSuggestOption<SuggestIndex> option : options) {
                SuggestIndex suggestIndex = option.source();
                String title = suggestIndex.getTitle();
                stringList.add(title);
            }
        }
    }
    private AlbumSearchResponseVo ParseSearchResponse(SearchResponse<AlbumInfoIndex> indexSearchResponse, AlbumIndexQuery albumIndexQuery) {
        AlbumSearchResponseVo albumSearchResponseVo = new AlbumSearchResponseVo();

        albumSearchResponseVo.setTotal(indexSearchResponse.hits().total().value());
        albumSearchResponseVo.setPageSize(albumIndexQuery.getPageSize());
        albumSearchResponseVo.setPageNo(albumIndexQuery.getPageNo());
      // 总记录数
        Integer totalPage = PageUtil.totalPage(Long.valueOf(indexSearchResponse.hits().total().value()).intValue(), albumIndexQuery.getPageSize());		// hutool工具类
        albumSearchResponseVo.setTotalPages(totalPage.longValue());

        List<Hit<AlbumInfoIndex>> hits = indexSearchResponse.hits().hits();
        List<AlbumInfoIndexVo> albumInfoIndexVoList = hits.stream().map(hit -> {
            AlbumInfoIndex albumInfoIndex = hit.source();
            AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
            BeanUtils.copyProperties(albumInfoIndex, albumInfoIndexVo);
            Map<String, List<String>> highlight = hit.highlight();
            if(!CollectionUtils.isEmpty(highlight)){
                List<String> albumTitle = highlight.get("albumTitle");
                if(!CollectionUtils.isEmpty(albumTitle)){
                    String highlightAlbumTitle = albumTitle.get(0);
                    albumInfoIndexVo.setAlbumTitle(highlightAlbumTitle);
                }
                List<String> albumIntro = highlight.get("albumIntro");
                if(!CollectionUtils.isEmpty(albumIntro)){
                    String highlightAlbumIntro = albumIntro.get(0);
                    albumInfoIndexVo.setAlbumIntro(highlightAlbumIntro);
                }
            }
            return albumInfoIndexVo;
        }).toList();
        albumSearchResponseVo.setList(albumInfoIndexVoList);
        return albumSearchResponseVo;
    }

    private SearchRequest buildSearchRequest(AlbumIndexQuery albumIndexQuery) {
        BoolQuery.Builder boolBuilder = new BoolQuery.Builder();
        if(!StringUtils.isEmpty(albumIndexQuery.getKeyword())){
            boolBuilder.must(builder->builder.multiMatch(QueryBuilders.multiMatch().fields("albumTitle", "albumIntro").query(albumIndexQuery.getKeyword()).build()));
        }else {
            boolBuilder.must(builder -> builder.matchAll(QueryBuilders.matchAll().build()));
        }

        if(albumIndexQuery.getCategory1Id() != null){
            boolBuilder.must(builder->builder.term(QueryBuilders.term().field("category1Id").value(albumIndexQuery.getCategory1Id()).build()));
        }
        if(albumIndexQuery.getCategory2Id() != null){
            boolBuilder.must(builder->builder.term(QueryBuilders.term().field("category2Id").value(albumIndexQuery.getCategory2Id()).build()));
        }
        if(albumIndexQuery.getCategory3Id() != null){
            boolBuilder.must(builder->builder.term(QueryBuilders.term().field("category3Id").value(albumIndexQuery.getCategory3Id()).build()));
        }

        if (!CollectionUtils.isEmpty(albumIndexQuery.getAttributeList())){
            for (String attribute : albumIndexQuery.getAttributeList()) {
                BoolQuery boolQuery = QueryBuilders.bool().must(builder->builder.term(QueryBuilders.term().field("attributeValueIndexList.attributeId").value(attribute.split(":")[0]).build()))
                        .must(builder->builder.term(QueryBuilders.term().field("attributeValueIndexList.valueId").value(attribute.split(":")[1]).build())).build();
                Query query = new Query.Builder().bool(boolQuery).build();
                NestedQuery nestedQuery = QueryBuilders.nested().path("attributeValueIndexList").query(query).build();
                boolBuilder.must(builder->builder.nested(nestedQuery));
            }
        }
        Query query = new Query.Builder().bool(boolBuilder.build()).build();
        FieldSort fieldSort = null;
        if(!StringUtils.isEmpty(albumIndexQuery.getOrder())){
            String order = albumIndexQuery.getOrder();
            SortOrder sortOrder = order.split(":")[1].equalsIgnoreCase("desc") ? SortOrder.Desc : SortOrder.Asc;
            switch(order.split(":")[0]){
                case "1":
                    fieldSort = new FieldSort.Builder().field("hotScore").order(sortOrder).build();
                    break;
                case "2":
                    fieldSort = new FieldSort.Builder().field("playStatNum").order(sortOrder).build();
                    break;
                case "3":
                    fieldSort = new FieldSort.Builder().field("createTime").order(sortOrder).build();
                    break;
            }
        }else {
            fieldSort = new FieldSort.Builder().field("hotScore").order(SortOrder.Desc).build();
        }
        SortOptions sortOptions = new SortOptions.Builder().field(fieldSort).build();
        SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder();
        if(!StringUtils.isEmpty(albumIndexQuery.getKeyword())){
            HighlightField highlightFieldAlbumTitle = new HighlightField.Builder().preTags("<font color='red'>").postTags("</font>").build();
            HighlightField highlightFieldAlbumIntro =  new HighlightField.Builder().preTags("<font color='red'>").postTags("</font>").build();
            Highlight highlight = new Highlight.Builder().fields("albumTitle",highlightFieldAlbumTitle).fields("albumIntro",highlightFieldAlbumIntro).build();
            searchRequestBuilder.highlight(highlight);
        }
        SearchRequest request = searchRequestBuilder.index("albuminfo").query(query).sort(sortOptions ).from((albumIndexQuery.getPageNo()-1)*albumIndexQuery.getPageSize()).size(albumIndexQuery.getPageSize()).build();
        log.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"+request.toString());
        return request;
    }
}